﻿using System.Net;
using System.Net.Mail;
using Microsoft.Extensions.Logging;
using Attachment = System.Net.Mail.Attachment;

namespace Devonline.AspNetCore;

/// <summary>
/// Smtp 邮件发送服务
/// </summary>
/// <typeparam name="TEmail">邮件数据类型</typeparam>
/// <typeparam name="TKey">主键类型</typeparam>
public class SmtpMailService<TEmail, TKey> : IEmailService<TEmail, TKey> where TEmail : Email<TKey> where TKey : IConvertible
{
    protected readonly ILogger<SmtpMailService<TEmail, TKey>> _logger;
    private readonly SmtpMailEndpoint _endpoint;
    private readonly SmtpClient _smtpClient;
    private readonly MailMessage _mailMessage;

    public SmtpMailService(ILogger<SmtpMailService<TEmail, TKey>> logger, SmtpMailEndpoint endpoint)
    {
        _logger = logger;
        _endpoint = endpoint;
        if (string.IsNullOrWhiteSpace(_endpoint.Host))
        {
            throw new ArgumentNullException(nameof(_endpoint.Host), "缺少必须的邮件服务器地址");
        }

        if (_endpoint.Port <= 0 || _endpoint.Port > 65535)
        {
            _endpoint.Port = 25;
        }

        if (string.IsNullOrWhiteSpace(_endpoint.From))
        {
            throw new ArgumentNullException(nameof(_endpoint.From), "缺少必须的发件人");
        }

        _smtpClient = new SmtpClient(_endpoint.Host, _endpoint.Port ?? 25);
        _mailMessage = new MailMessage
        {
            From = new MailAddress(_endpoint.From)
        };

        if (_endpoint.UseCredentials)
        {
            _smtpClient.Credentials = new NetworkCredential(_endpoint.UserName, _endpoint.Password);
        }
    }

    /// <summary>
    /// 发送邮件
    /// </summary>
    /// <param name="mail">邮件对象</param>
    public virtual void Send(TEmail mail)
    {
        Validate(mail);
        mail.SendTime = DateTime.UtcNow;
        _smtpClient.Send(_mailMessage);
    }
    /// <summary>
    /// 发送邮件
    /// </summary>
    /// <param name="mail">邮件对象</param>
    /// <param name="userToken">用户令牌</param>
    public virtual void SendAsync(TEmail mail, object? userToken)
    {
        Validate(mail);
        mail.SendTime = DateTime.UtcNow;
        _smtpClient.SendAsync(_mailMessage, userToken);
    }
    /// <summary>
    /// 发送邮件
    /// </summary>
    /// <param name="mail">邮件对象</param>
    public virtual Task SendAsync(TEmail mail)
    {
        Validate(mail);
        mail.SendTime = DateTime.UtcNow;
        return _smtpClient.SendMailAsync(_mailMessage);
    }
    /// <summary>
    /// 发送邮件
    /// </summary>
    /// <param name="mail">邮件对象</param>
    /// <param name="cancellationToken">取消执行令牌</param>
    public virtual Task SendAsync(TEmail mail, CancellationToken cancellationToken)
    {
        Validate(mail);
        mail.SendTime = DateTime.UtcNow;
        return _smtpClient.SendMailAsync(_mailMessage, cancellationToken);
    }

    /// <summary>
    /// 释放资源
    /// </summary>
    public virtual void Dispose()
    {
        _mailMessage.Dispose();
        _smtpClient.Dispose();
        GC.SuppressFinalize(this);
    }
    /// <summary>
    /// 验证邮件内容
    /// </summary>
    /// <param name="mail">邮件对象</param>
    /// <exception cref="ArgumentNullException"></exception>
    private void Validate(TEmail mail)
    {
        if (string.IsNullOrWhiteSpace(mail.To))
        {
            throw new ArgumentNullException(nameof(mail.To), "缺少必须的接收人");
        }

        if (!string.IsNullOrWhiteSpace(mail.To))
        {
            foreach (string to in mail.To.Split(CHAR_SEMICOLON, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
            {
                _mailMessage.To.Add(to);
            }
        }

        if (!string.IsNullOrWhiteSpace(mail.CC))
        {
            foreach (string cc in mail.CC.Split(CHAR_SEMICOLON, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
            {
                _mailMessage.CC.Add(cc);
            }
        }

        if (!string.IsNullOrWhiteSpace(mail.Bcc))
        {
            foreach (string bcc in mail.Bcc.Split(CHAR_SEMICOLON, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
            {
                _mailMessage.Bcc.Add(bcc);
            }
        }

        if (!string.IsNullOrWhiteSpace(mail.Attachments))
        {
            foreach (string attachment in mail.Attachments.Split(CHAR_SEMICOLON, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
            {
                _mailMessage.Attachments.Add(new Attachment(attachment.GetAbsolutePath()));
            }
        }

        _logger.LogInformation($"The system will send the email, the email content is: {mail}");
    }
}

/// <summary>
/// Smtp 邮件发送服务
/// 如果邮件带有附件, 附件发送前先要有正确的文件路径, 可以是相对路径
/// </summary>
public class SmtpMailService(ILogger<SmtpMailService> logger, SmtpMailEndpoint endpoint) : SmtpMailService<Email, string>(logger, endpoint), IEmailService { }